/* 
 * Copyright (c) 2004-2005 Massachusetts Institute of Technology. This code was
 * developed as part of the Haystack (http://haystack.lcs.mit.edu/) research 
 * project at MIT. Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation files (the 
 * "Software"), to deal in the Software without restriction, including without 
 * limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom
 * the Software is furnished to do so, subject to the following conditions: 
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */
/*
 * Created on Jun 13, 2004
 *
 */
package edu.mit.csail.relo.jdt.parts;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Transformer;
import org.apache.log4j.Logger;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.openrdf.model.Resource;

import edu.mit.csail.relo.eclipse.gef.EditPartListener2;
import edu.mit.csail.relo.jdt.CodeUnit;
import edu.mit.csail.relo.jdt.ReloJDTPlugin;
import edu.mit.csail.relo.modelBridge.Artifact;
import edu.mit.csail.relo.modelBridge.DerivedArtifact;
import edu.mit.csail.relo.parts.ArtifactEditPart;
import edu.mit.csail.relo.parts.MoreItemsEditPart;

/**
 * This class should really be able to go up higher in the chain, however, we
 * don't have multiple inheritance (and we need CodeUnitEditPart functionality)
 * so we are putting it after CodeUnitEditPart
 * 
 * @author vineet
 */
public abstract class CompartmentedCodeUnitEditPart extends CodeUnitEditPart {
	static final Logger logger = ReloJDTPlugin.getLogger(CompartmentedCodeUnitEditPart.class);


    public static abstract class CompartmentCodeUnit extends DerivedArtifact {
        public CompartmentCodeUnit(CodeUnit cu) {
            super(cu);
        }
    }

    public static abstract class CompartmentEditPart extends MoreItemsEditPart {
        public abstract void registerTypes(Map<Resource, CompartmentEditPart> compartmentMap);
    }
    
    // detail level
    final int labelWithCompartments = getMinimalDL();

    /* (non-Javadoc)
     * @see edu.mit.csail.relo.parts.ArtifactEditPart#getDLStr(int)
     */
    @Override
    public String getDLStr(int dl) {
        if (dl == labelWithCompartments)
            return "labelWithCompartments";
        return super.getDLStr(dl);
    }

    @Override
    protected void updateMembers(int newDL) {
        if (newDL >= currDL) {
            if (newDL >= labelWithCompartments) realizeCompartments();
        }
        if (newDL < currDL) {
            if (newDL == labelWithCompartments) {
                emptyCompartments();
            } else if (newDL < labelWithCompartments) {
                hideCompartments();
            }
        }
    }

    // in the below two we only store ComparmentTypes, each of which can support
    // one or more types of Artifacts, note the order in compartmentTypes matters
    Map<Resource, Class> compartmentGenerator = new HashMap<Resource, Class> (5);
    List<Resource> compartmentTypes = new ArrayList<Resource> (5);

    Map<Resource, CompartmentEditPart> compartmentMap = new HashMap<Resource, CompartmentEditPart> (5);
    
    protected Set<CompartmentEditPart> getComparments() {
        // return as set since values can have repeats
        return new HashSet<CompartmentEditPart>(compartmentMap.values());
    }
    

    
    protected void realizeCompartments() {
        Iterator<Resource> resIt = compartmentTypes.iterator();
        while (resIt.hasNext()) {
            Resource compartmentType = resIt.next();
            CompartmentEditPart cep = compartmentMap.get(compartmentType);
            if (cep == null) {
                cep = realizeCompartment(compartmentType);
                if (cep != null) cep.registerTypes(compartmentMap);
            } else if (!this.getChildren().contains(cep)) {
                // it was realized but has since been removed
                CompartmentCodeUnit ccu = (CompartmentCodeUnit) cep.getArtifact();
                appendModelAndChild(ccu, cep);
            } else {
                // already been realized...
            }
        }
    }
    private CompartmentEditPart realizeCompartment(Resource compartmentType) {
        Class compartmentClass = compartmentGenerator.get(compartmentType);
        CompartmentEditPart cep = null;
        try {
            CompartmentCodeUnit ccu = (CompartmentCodeUnit) compartmentClass.getConstructors()[0].newInstance(getCU());
            cep = (CompartmentEditPart) appendModelAndChild(ccu);
        } catch (Throwable e) {
            logger.error("Unexpected exception", e);
            return null;
        }
        cep.addEditPartListener(new EditPartListener2.Stub() {
            @Override
            public void childAdded(EditPart child, int index) {
                updateMoreButton();
            }
            @Override
            public void removedChild(EditPart child) {
                updateMoreButton();
            }
        });
        return cep;
    }

    protected void hideCompartments() {
        getModelChildren().clear();
        compartmentMap.clear();
        refresh();
    }

    protected void emptyCompartments() {
        List<EditPart> visChildren = getVisibleEditPartChildren();
        List<Object> txVisChildren = new ArrayList<Object>(visChildren);
        CollectionUtils.transform(txVisChildren, new Transformer(){
            public Object transform(Object input) {
                if (input instanceof ArtifactEditPart) {
                    return ((ArtifactEditPart)input).getArtifact();
                } else
                    return null;
            }});
        removeChildrenArtifacts(txVisChildren);
    }

    @Override
    protected List<EditPart> getVisibleEditPartChildren() {
        List<EditPart> childrenEP = new ArrayList<EditPart> (20);
        // use hashset since the map can 
        for (CompartmentEditPart cep : getComparments() ) {
            childrenEP.addAll(cep.getChildrenAsTypedList());
        }
        return childrenEP;
    }
    
    @Override
    public AbstractGraphicalEditPart appendModelAndChild(Object child, AbstractGraphicalEditPart childAGEP) {
        if (child instanceof DerivedArtifact)
            return super.appendModelAndChild(child, childAGEP);

        realizeCompartments();
        Resource childType = ((Artifact) child).getType(getRepo());
        CompartmentEditPart cep = compartmentMap.get(childType);
        if (cep != null) {
            return cep.appendModelAndChild(child, childAGEP);
        }
        
        // there are no handlers, route to parents
        return super.appendModelAndChild(child, childAGEP);
    }
    
}
